home *** CD-ROM | disk | FTP | other *** search
- Tornado renderers
- -=-=-=-=-=-=-=-=-
- A tornado renderer provides facilities for rendering files. They are
- automatically called upon by the shell to render a file as it presents itself
- and should they not be already in memory they can be loaded in and cached by
- tornado during the execution of the rendition.
-
- A tornado renderer is similar to a normal tornado extension module, except:
-
- (i) In it's initialise entry, it must declare itself as a renderer.
- (ii) It must live in <Tornado$Renderers> rather than <Tornado$Extensions>.
- (iii) It must provide all the mandatory entries as specified by the file
- type it is supporting [1].
- (iv) It must be able to render more than one file at once [2]
- (v) It must be able to render more than one part of a file at once [2]
- (vi) It must be able to handle being loaded in and quitted repeatedly, and
- at any stage - even if that be in the middle of a rendition [3].
-
- [1]: These entries are laid out in a list issued by me, the guy who's
- designing and writing all this stuff. If you want to add a filetype, you draw
- up a suitable addendum to the list, and email/post it to me. I review it, and
- if I don't like it I'll refuse it.
- This leaves you with two options. (i) You can go away and improve it or
- (ii) you can go ahead and implement it anyway.
- The first option is the correct one. The second will land you in trouble.
- I will hinder any developments/improvements on any filetype not approved by
- me. Is this not a bit arrogant? Yes, it is, but it's my operating system and
- I'll have it go the way *I* want it. You don't like it - go away and write
- your own operating system.
- Anyway, there's always the general filetypes which you can extend if you
- wish.
-
- [2]: In fact, this is implemented by you writing a very clever piece of code
- capable of being multithreaded (not fun). Tornado will handle all the hard
- work of getting your code to do the work, although this requires cooperation
- from you:
- (i) You cannot use any static workspace unless you have developed some
- brilliant way of allowing any parts of your code to access it at any time and
- even while another thread of your code is in the middle of using it.
- (ii) Thus, you should use dynamic workspace allocation. See below.
-
- [3]: Ie; your renderer must be able to tidy itself and all its threads up
- should .finalise suddenly be called. As your renderer code may be
- initialised, quit, and then initialised again many times in one second, it
- must *not* leave any wkspace unfreed as this will obviously fill up memory
- very quickly.
-
- Technical stuff:
- -=-=-=-=-=-=-=-=
- Right, here's how tornado interfaces with your renderer:
-
- In the tornado .initialise, you must call Tornado_RegisterRenderer, passing
- your details (see below for parameter block).
-
- In the .threadinit entry, you should claim a piece of wkspace that will be
- enough for a thread to work with entirely if possible. However, if this
- is not possible (max memory that might be required is several K for each
- thread for example), check what the tornado config says - use more memory, or
- use less memory (Tornado_Config). If it says use less memory, assume that
- there is more CPU power available and dynamically alter the block's size
- during the thread's execution (even though this is a non-preemptable action
- and everything will be held up as a result). If it doesn't, allocate the most
- the thread could possibly need. Later versions of tornado may automate this
- regulation of memory for you later but currently it is up to you to mark what
- method you are following and follow it accordingly.
-
- The .threadexec entry will be entered as follows:
- R8 = undefined as yet, but can be ignored by your code (ie; overwritable)
- R9 = a binary set of flags:
- bits 0,1: unset
- bits 2-25: reserved
- bits 26-31: unset
- You may overwrite these, but it is advisable you don't (see below).
- R10 = a pointer to the file data you are rendering
- R11 = a pointer to your thread's workspace as set up by your init routine
- R12 = a pointer to the workspace of your module (should only be used for
- reference!)
- R13 = a private stack peculiar to your thread which is empty (64 bytes long,
- so don't push too much onto it, and allow for routines you call to use it)
- R14 = a link back to tornado when you have finished the rendition
-
- Code is in USR mode (no need to stack R14), all interrupts enabled.
-
- This leaves you R0-R7 for your own private use. This is admittedly grim, but
- there's not much that can be done about it.
-
- There are a few things that should be noted about multithreading as most
- Acorn programmer's probably aren't familiar with it (nor am I - I'm not even
- sure if this will work yet!):
-
- Firstly, your code can be interrupted at *any* time. You cannot rely on
- registers R10-R13 to remain static. In other words, you can't do this:
-
- ADD R0,R11,R3
- STR R5,[R0,#44]
-
- You see, if R10 gets changed between the ADD and the STR, the R5 will be
- stored at the wrong address, possibly causing an Address Exception or at the
- least corrupting someone's data (usually your own).
-
- Here's how you would rewrite this:
-
- ADD R0,R3,#44
- STR R5,[R11,R0]
-
- A simple little change, no difference normally, but when multitasking
- preemptively makes a world of difference. The same goes for offsetting into
- the file, using a register as an offset pointer - always LDR/STR with R10 as
- one of the parameters.
-
- But what happens if this isn't possible eg; building coordinates for plotting
- onto the screen?
-
- Well, the solution is to halt multitasking. It's not a good idea, and should
- be avoided if at all possible, but often it simply isn't. Simply place a SWI
- "XOS_EnterOS" in front of your non-preemptable code, and a TEQP R9,#0; MOV
- R0,R0 after it, assuming you've left R9 intact.
-
- Another point in all this is speed. Renditions must be *FAST*. That's why you
- *MUST* write your rendition code in assembler (you'll note how C or anything
- else would die horribly in the above circumstances). Optimising your code
- (rolling out loops, organising instructions in the most efficient manner etc)
- really does pay dividends in redraw speed, and it is this above all that
- marks out one application over another (eg; Zap versus Edit - redraw is so
- blindingly fast it hurts, especially on the older versions without
- ZapRedraw). To this end, it is important to note the CPU cost of disabling
- preemption - calling SWI's is a notoriously slow affair (much worse on RO3+
- than RO2 I might add) at around 50-70 instructions; added to this is the cost
- in everything being held up, followed by another two otherwise unnecessary
- instructions.
-
- Another instance of this is using LDR/STR against LDM/STM. The latter is much
- quicker for multiple regs. So much quicker in fact that for any decent amount
- of memory shifting it may be quicker to disable preemption for it (you must
- as you can't index the position register). There may be support for multiple
- get/put's in later versions of the multitasker, but as yet you can usually
- not use them, and such support will not affect threads written this way now.
-
-